home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 04 - 1988 / 04.01 Jan 88 / typing battle source / TypingBattle Source < prev    next >
Encoding:
Text File  |  1987-11-27  |  14.4 KB  |  613 lines  |  [TEXT/PJMM]

  1. {        TypingBattle by Steve Sheets 11/15/87        }
  2.  
  3. {    Simple Demonstration of Multible Keyboards.        }
  4.  
  5. PROGRAM TypingBattle;
  6.  
  7.     USES
  8.         MultiLine;                {Unit to handle simple editing}
  9.  
  10.     CONST
  11.         MaxAllow = 5;        {Max Number of Players}
  12.  
  13.         AboutID = 600;        {Various Resource IDs}
  14.         TextID = 600;
  15.         LineID = 601;
  16.         AppleMenuID = 1;
  17.         FileMenuID = 2;
  18.         EditMenuID = 3;
  19.  
  20.         WindowH = 480;        {Placement Constants}
  21.         VEdge = 40;
  22.         CenterTop = 40;
  23.         Edge = 10;
  24.         Hi = 20;
  25.         PlayOff = 50;
  26.         TTop = -15;
  27.         RTop = 5;
  28.         WTop = 35;
  29.  
  30.         TimeCount = 180;    {Timer to start round}
  31.  
  32. {Variables: Menus, Done Flag, Number Players, Main Window, Status of game,}
  33. {    Number of Samples, Number Players done, lots strings, arrays holding Players}
  34. {    Scores, Who's Done, Names & Bus IDs and finally Edit fields holding}
  35. {    Message, Names & Text.}
  36.     VAR
  37.         AppleMenu, FileMenu, EditMenu : MenuHandle;
  38.         Done : boolean;
  39.         theNum : integer;
  40.         MyWindow : WindowPtr;
  41.         Status, NumSamples, NumDone : integer;
  42.         WelStr, ScoreStr, PreStr, WinStr, PressStr : str255;
  43.         NoOneStr, EnterStr, SepStr, theTitle : str255;
  44.         Score : ARRAY[1..MaxAllow] OF integer;
  45.         isDone : ARRAY[1..MaxAllow] OF boolean;
  46.         theName : ARRAY[1..MaxAllow] OF str255;
  47.         theBus : ARRAY[1..MaxAllow] OF integer;
  48.         MessRec : MLRec;
  49.         NameRec, TextRec : ARRAY[1..MaxAllow] OF MLRec;
  50.  
  51.  
  52. {Returns Number Of Samples (ie. Number of strings in the STR# resource.}
  53.     FUNCTION GetNumSamples : integer;
  54.         TYPE
  55.             WP = ^integer;
  56.             WH = ^WP;
  57.         VAR
  58.             W : WH;
  59.     BEGIN
  60.         W := POINTER(GetResource('STR#', TextID));
  61.         GetNumSamples := W^^;
  62.         ReleaseResource(POINTER(W));
  63.     END;
  64.  
  65. {Given M (Longint Message field of Keydown event), return B (Bus ID),}
  66. {    V (Virtual code) & C (Key pressed).}
  67.     PROCEDURE CalcKey (M : longint;
  68.                                     VAR B, V : integer;
  69.                                     VAR C : char);
  70.     BEGIN
  71.         C := Chr(M MOD 256);
  72.         B := (M DIV 65536) MOD 256;
  73.         V := (M DIV 256) MOD 256;
  74.     END;
  75.  
  76. {Do About Box.}
  77.     PROCEDURE DoAbout;
  78.         VAR
  79.             n : integer;
  80.     BEGIN
  81.         n := Alert(AboutID, NIL);
  82.     END;
  83.  
  84. {Initialize variables}
  85.     PROCEDURE DoSetup;
  86.     BEGIN
  87.         GetIndString(WelStr, LineID, 1);
  88.         GetIndString(ScoreStr, LineID, 2);
  89.         GetIndString(PreStr, LineID, 3);
  90.         GetIndString(WinStr, LineID, 4);
  91.         GetIndString(PressStr, LineID, 5);
  92.         GetIndString(NoOneStr, LineID, 6);
  93.         GetIndString(theTitle, LineID, 7);
  94.         GetIndString(EnterStr, LineID, 8);
  95.         GetIndString(SepStr, LineID, 9);
  96.         NumSamples := GetNumSamples;
  97.  
  98.  
  99.         AppleMenu := GetMenu(AppleMenuID);
  100.         AddResMenu(AppleMenu, 'DRVR');
  101.         InsertMenu(AppleMenu, 0);
  102.  
  103.         FileMenu := GetMenu(FileMenuID);
  104.         InsertMenu(FileMenu, 0);
  105.  
  106.         EditMenu := GetMenu(EditMenuID);
  107.         InsertMenu(EditMenu, 0);
  108.  
  109.         DrawMenuBar;
  110.  
  111.         InitCursor;
  112.  
  113.         MyWindow := NIL;
  114.         Done := false;
  115.     END;
  116.  
  117. {Given an integer H & V, make a centered rectange R.}
  118.     PROCEDURE MakeRect (VAR R : rect;
  119.                                     h, v : integer);
  120.         VAR
  121.             N : integer;
  122.     BEGIN
  123.         N := (screenbits.bounds.right - screenbits.bounds.left - H) DIV 2;
  124.         SetRect(R, N, VEdge, N + H, VEdge + V);
  125.     END;
  126.  
  127. {Handle Special Keys, return true if none was pressed.  In this case, only}
  128. {    handle Help by calling About Box.  Note the Hex Codes.}
  129.     FUNCTION NotSpecKeys (Virtual : integer) : boolean;
  130.     BEGIN
  131.         IF Virtual = $72 THEN
  132.             BEGIN
  133.                 DoAbout;
  134.                 NotSpecKeys := false;
  135.             END
  136.         ELSE
  137.             NotSpecKeys := true;
  138.     END;
  139.  
  140. {Finds out who is playing (ie. set Names & Bus IDs) or quit game (ie. Done true).}
  141.     PROCEDURE GetPlayers;
  142.         CONST
  143.             ConV = 85;
  144.             ConH = 360;
  145.             DLOff = 15;
  146.             kOff = 25;
  147.             kTop = 15;
  148.             kHi = 20;
  149.             LineLeft = 20;
  150.             butBot = -15;
  151.             OKLeft = 90;
  152.             QUITLeft = 210;
  153.             Voff = 15;
  154.         VAR
  155.             tempPort : Grafptr;
  156.             myW : WindowPtr;
  157.             cont, F2 : boolean;
  158.             tempEvent : EventRecord;
  159.             tempWindow : windowptr;
  160.             tempCode, tempVirtual : integer;
  161.             tempChar : char;
  162.             OKcon, QUITcon, tempCon : ControlHandle;
  163.             Lines : ARRAY[1..MaxAllow] OF MLRec;
  164.  
  165.         PROCEDURE DoBox (V : integer);
  166.             VAR
  167.                 R2 : Rect;
  168.         BEGIN
  169.             WITH Lines[V] DO
  170.                 BEGIN
  171.                     R2.left := Fr.left - 1;
  172.                     R2.right := Fr.right + 1;
  173.                     R2.top := Fr.top - 1;
  174.                     R2.bottom := Fr.bottom + 1;
  175.                     IF V > theNum THEN
  176.                         PenPat(Gray);
  177.                     FrameRect(R2);
  178.                     PenPat(Black);
  179.                 END;
  180.         END;
  181.  
  182.         PROCEDURE CheckOK;
  183.         BEGIN
  184.             IF (theNum > 0) AND (theNum <= MaxAllow) THEN
  185.                 HiliteControl(OKcon, 0)
  186.             ELSE
  187.                 HiliteControl(OKcon, 255);
  188.         END;
  189.  
  190.         PROCEDURE RegKey (V : integer;
  191.                                         C : Char);
  192.             VAR
  193.                 count, L : integer;
  194.         BEGIN
  195.             count := 0;
  196.             FOR L := 1 TO theNum DO
  197.                 IF theBus[L] = v THEN
  198.                     count := L;
  199.             IF count = 0 THEN
  200.                 BEGIN
  201.                     IF (theNum < MaxAllow) AND (Ord(C) >= 32) THEN
  202.                         BEGIN
  203.                             theNum := theNum + 1;
  204.                             theBus[theNum] := V;
  205.                             Lines[theNum].St := ' ';
  206.                             Lines[theNum].St[1] := C;
  207.                             Lines[theNum].Cr := '_';
  208.                             MLreset(Lines[theNum]);
  209.                             DoBox(theNum);
  210.                             CheckOk;
  211.                         END;
  212.                 END
  213.             ELSE
  214.                 BEGIN
  215.                     MLchar(Lines[count], C);
  216.                     IF Lines[count].St = '' THEN
  217.                         BEGIN
  218.                             IF count <> theNum THEN
  219.                                 BEGIN
  220.                                     FOR L := count TO theNum - 1 DO
  221.                                         BEGIN
  222.                                             MLtext(Lines[L], Lines[L + 1].St);
  223.                                             theBus[L] := theBus[L + 1];
  224.                                         END;
  225.                                     Lines[theNum].St := '';
  226.                                 END;
  227.                             Lines[theNum].Cr := ' ';
  228.                             MLreset(Lines[theNum]);
  229.                             theNum := theNum - 1;
  230.                             DoBox(theNum + 1);
  231.                             CheckOk;
  232.                         END
  233.                 END;
  234.         END;
  235.  
  236.         PROCEDURE DoGPSetup;
  237.             VAR
  238.                 nn, count : integer;
  239.                 S : str255;
  240.                 Bx : rect;
  241.         BEGIN
  242.             nn := ConV + (MaxAllow * kOff);
  243.             MakeRect(Bx, ConH, nn);
  244.             myW := NewWindow(NIL, Bx, '', true, 1, POINTER(-1), false, 0);
  245.             SetPort(myW);
  246.             SetRect(Bx, OKLeft, nn - 20 + butBot, OKLeft + 60, nn + butBot);
  247.             GetIndString(S, LineID, 10);
  248.             OKcon := NewControl(myW, Bx, S, true, 0, 0, 0, 0, 0);
  249.             SetRect(Bx, QUITLeft, nn - 20 + butBot, QUITLeft + 60, nn + butBot);
  250.             GetIndString(S, LineID, 11);
  251.             QUITcon := NewControl(myW, Bx, S, true, 0, 0, 0, 0, 0);
  252.             cont := false;
  253.             theNum := 0;
  254.  
  255.             FOR count := 1 TO MaxAllow DO
  256.                 BEGIN
  257.                     nn := (count * kOff) + DLOff;
  258.                     SetRect(Bx, LineLeft, nn, ConH - LineLeft, nn + kHi);
  259.                     MLinit(Lines[count], '', '', ' ', Bx, false);
  260.                 END;
  261.         END;
  262.  
  263.     BEGIN
  264.         DoGPSetup;
  265.         CheckOk;
  266.         REPEAT
  267.             SystemTask;
  268.             IF GetNextEvent(everyEvent, tempEvent) THEN
  269.                 BEGIN
  270.                     IF tempEvent.what = mouseDown THEN
  271.                         BEGIN
  272.                             F2 := true;
  273.                             IF FindWindow(tempEvent.where, tempWindow) = inContent THEN
  274.                                 BEGIN
  275.                                     GlobalToLocal(tempEvent.where);
  276.                                     IF FindControl(tempEvent.where, myW, tempCon) <> 0 THEN
  277.                                         IF TrackCOntrol(tempCon, tempEvent.where, NIL) <> 0 THEN
  278.                                             BEGIN
  279.                                                 IF tempCon = QUITcon THEN
  280.                                                     BEGIN
  281.                                                         Done := true;
  282.                                                         F2 := false;
  283.                                                     END
  284.                                                 ELSE IF tempCon = OKcon THEN
  285.                                                     BEGIN
  286.                                                         Cont := true;
  287.                                                         FOR tempCode := 1 TO theNum DO
  288.                                                             theName[tempCode] := Lines[tempCode].St;
  289.                                                         F2 := false;
  290.                                                     END;
  291.                                             END
  292.                                         ELSE
  293.                                             F2 := false;
  294.                                 END;
  295.                             IF F2 THEN
  296.                                 sysbeep(1);
  297.                         END;
  298.                     IF tempEvent.what = keydown THEN
  299.                         BEGIN
  300.                             CalcKey(tempEvent.message, tempCode, tempVirtual, tempChar);
  301.                             IF NotSpecKeys(tempVirtual) THEN
  302.                                 RegKey(tempCode, tempChar);
  303.                         END;
  304.                     IF tempEvent.what = updateEvt THEN
  305.                         IF myW = WindowPtr(tempEvent.message) THEN
  306.                             BEGIN
  307.                                 GetPort(tempPort);
  308.                                 SetPort(myW);
  309.                                 BeginUpdate(myW);
  310.                                 MoveTo((ConH - StringWidth(EnterStr)) DIV 2, kTop + Voff);
  311.                                 DrawString(EnterStr);
  312.                                 FOR tempCode := 1 TO MaxAllow DO
  313.                                     BEGIN
  314.                                         MLupdate(Lines[tempCode]);
  315.                                         DoBox(tempCode);
  316.                                     END;
  317.                                 DrawControls(myW);
  318.                                 EndUpdate(myW);
  319.                                 SetPort(tempPort);
  320.                             END;
  321.                 END;
  322.         UNTIL cont OR done;
  323.         KillControls(myW);
  324.         DisposeWindow(myW);
  325.     END;
  326.  
  327. {Dispose Window (if any), get Players (if any), if so create the Window and}
  328. {    Edit fields for the next game.}
  329.     PROCEDURE DoConfigure;
  330.         VAR
  331.             tempRect : Rect;
  332.             flag : boolean;
  333.             count : integer;
  334.             Bx : rect;
  335.     BEGIN
  336.         IF MyWindow <> NIL THEN
  337.             BEGIN
  338.                 DisposeWindow(MyWindow);
  339.                 MyWindow := NIL;
  340.             END;
  341.  
  342.         GetPlayers;
  343.  
  344.         IF NOT done THEN
  345.             BEGIN
  346.                 MakeRect(tempRect, WindowH, WTop + (theNum * PlayOff));
  347.                 MyWindow := NewWindow(NIL, tempRect, theTitle, false, 1, POINTER(-1), false, 0);
  348.                 SetPort(MyWindow);
  349.  
  350.                 Bx.left := Edge;
  351.                 Bx.right := WindowH - Edge;
  352.                 Bx.top := Edge;
  353.                 Bx.bottom := Bx.top + Hi;
  354.                 MLinit(MessRec, '', '', ' ', Bx, false);
  355.                 FOR count := 1 TO theNum DO
  356.                     BEGIN
  357.                         Bx.top := (count * PlayOff) + TTop;
  358.                         Bx.bottom := Bx.top + Hi;
  359.                         Score[count] := 0;
  360.                         MLinit(NameRec[count], CONCAT(theName[count], ScoreStr), '', ' ', Bx, false);
  361.                         Bx.top := (count * PlayOff) + RTop;
  362.                         Bx.bottom := Bx.top + Hi;
  363.                         MLinit(TextRec[count], '', '', ' ', Bx, true);
  364.                     END;
  365.  
  366.                 ShowWindow(MyWindow);
  367.             END;
  368.     END;
  369.  
  370. {Start a game by setting status to 1, setting correct message, clearing score,}
  371. {    text and done flags.}
  372.     PROCEDURE DoStart;
  373.         VAR
  374.             count : integer;
  375.             tempPort : GrafPtr;
  376.     BEGIN
  377.         GetPort(tempPort);
  378.         SetPort(MyWindow);
  379.         Status := 1;
  380.         MLtext(MessRec, WelStr);
  381.         FOR count := 1 TO theNum DO
  382.             BEGIN
  383.                 Score[count] := 0;
  384.                 MLtext(NameRec[count], '0');
  385.                 MLtext(TextRec[count], '');
  386.                 IsDone[count] := false;
  387.             END;
  388.         SetPort(tempPort);
  389.     END;
  390.  
  391. {Handle updating the window by calling Edit fields update.}
  392.     PROCEDURE DoUp;
  393.         VAR
  394.             count : integer;
  395.             tempR : rect;
  396.     BEGIN
  397.         SetRect(tempR, 0, 0, 1000, 1000);
  398.         EraseRect(tempR);
  399.         MLupdate(MessRec);
  400.         FOR count := 1 TO theNum DO
  401.             BEGIN
  402.                 MLupdate(NameRec[count]);
  403.                 MLupdate(TextRec[count]);
  404.             END;
  405.     END;
  406.  
  407. {Flush the Event buffer of Keydowns.}
  408.     PROCEDURE FlushKeys;
  409.     BEGIN
  410.         FlushEvents(keyDownMask, 0);
  411.     END;
  412.  
  413. {Depending on Game Status, handle the Key down.  Who is Players number (not}
  414. {    bus ID).  Status 1 is everyone waiting for all Players to press return for}
  415. {    the next round.    Status 2 has everyone playing.}
  416.     PROCEDURE DoKey (Who : integer;
  417.                                     Key : char);
  418.         VAR
  419.             ll : longint;
  420.             Sp : str255;
  421.             count : integer;
  422.             dummy : boolean;
  423.             tempPort : GrafPtr;
  424.     BEGIN
  425.         GetPort(tempPort);
  426.         SetPort(MyWindow);
  427.         SetPort(MyWindow);
  428.  
  429.         IF (Status = 1) AND (Key = Chr(13)) THEN
  430.             BEGIN
  431. {Player Pressed return.}
  432.                 IsDone[who] := true;
  433.                 dummy := true;
  434.                 FOR count := 1 TO theNum DO
  435.                     dummy := dummy AND IsDone[count];
  436. {If everyone pressed return, wait awhile, then display the Sample, flush any}
  437. {    old key events & go to Status 2.}
  438.                 IF dummy THEN
  439.                     BEGIN
  440.                         MLtext(MessRec, PreStr);
  441.                         ll := Tickcount;
  442.                         NumDone := 0;
  443.                         FOR count := 1 TO theNum DO
  444.                             BEGIN
  445.                                 IsDone[count] := false;
  446.                                 MLtext(TextRec[count], '');
  447.                             END;
  448.                         count := (Random MOD NumSamples) + 1;
  449.                         GetIndString(Sp, TextID, count);
  450.                         WHILE tickcount < ll + TimeCount DO
  451.                             ;
  452.                         MLtext(MessRec, Sp);
  453.                         Status := 2;
  454.                         FlushKeys;
  455.                     END;
  456.             END
  457.         ELSE IF Status = 2 THEN
  458.             BEGIN
  459. {If player has not pressed return,}
  460.                 IF NOT IsDone[who] THEN
  461.                     BEGIN
  462.                         MLchar(TextRec[who], Key);
  463. {Handle the Key.}
  464.                         IF Key = Chr(13) THEN
  465.                             BEGIN
  466. {If return, he is done.}
  467.                                 NumDone := NumDone + 1;
  468.                                 IsDone[who] := true;
  469.                                 IF EqualString(TextRec[who].St, MessRec.St, true, false) THEN
  470.                                     BEGIN
  471. {Handle him winning.}
  472.                                         Status := 1;
  473.                                         Score[who] := Score[who] + LENGTH(MessRec.St);
  474.                                         MLtext(MessRec, CONCAT(WinStr, theName[who], PressStr));
  475.                                         NumToString(Score[who], Sp);
  476.                                         MLtext(NameRec[who], Sp);
  477.                                         FOR count := 1 TO theNum DO
  478.                                             IsDone[count] := false;
  479.                                     END
  480.                                 ELSE IF NumDone = theNum THEN
  481.                                     BEGIN
  482. {Handle no one winning.}
  483.                                         Status := 1;
  484.                                         MLtext(MessRec, NoOneStr);
  485.                                         FOR count := 1 TO theNum DO
  486.                                             IsDone[count] := false;
  487.                                     END;
  488.                             END;
  489.                     END;
  490.             END;
  491.         SetPort(tempPort);
  492.     END;
  493.  
  494. {Handle Menu.}
  495.     PROCEDURE MainMenu (tempResult : LONGINT);
  496.         VAR
  497.             tempInteger : integer;
  498.             tempStr : str255;
  499.     BEGIN
  500.         tempInteger := LoWord(tempResult);
  501.         CASE HiWord(tempResult) OF
  502.             AppleMenuID : 
  503.                 IF tempInteger = 1 THEN
  504.                     DoAbout
  505.                 ELSE
  506.                     BEGIN
  507.                         GetItem(appleMenu, tempInteger, tempStr);
  508.                         tempInteger := OpenDeskAcc(tempStr);
  509.                     END;
  510.             FileMenuID : 
  511.                 IF tempInteger IN [1, 2] THEN
  512.                     BEGIN
  513.                         IF tempInteger = 2 THEN
  514.                             DoConfigure;
  515.                         IF NOT done THEN
  516.                             DoStart;
  517.                     END
  518.                 ELSE IF tempInteger = 4 THEN
  519.                     Done := true;
  520.             EditMenuID : 
  521.                 IF NOT SystemEdit(tempInteger - 1) THEN
  522.                     sysbeep(1);
  523.             OTHERWISE
  524.         END;
  525.         HiliteMenu(0);
  526.     END;
  527.  
  528. {Main Event Loop.}
  529.     PROCEDURE DoMainLoop;
  530.         VAR
  531.             tempEvent : EventRecord;
  532.             tempWindow : windowptr;
  533.             tempCode, tempBus, tempVirtual : integer;
  534.             tempPort : Grafptr;
  535.             tempChar : Char;
  536.             tempRect : rect;
  537.     BEGIN
  538.         REPEAT
  539.             SystemTask;
  540.             IF GetNextEvent(everyEvent, tempEvent) THEN
  541.                 BEGIN
  542.                     CASE tempEvent.what OF
  543.                         mouseDown : 
  544.                             BEGIN
  545.                                 tempCode := FindWindow(tempEvent.where, tempWindow);
  546.                                 CASE tempCode OF
  547.                                     inMenuBar : 
  548.                                         MainMenu(MenuSelect(tempEvent.where));
  549.                                     inSysWindow : 
  550.                                         SystemClick(tempEvent, tempWindow);
  551.                                     inContent : 
  552.                                         IF tempWindow <> FrontWindow THEN
  553.                                             SelectWindow(tempWindow);
  554.                                     inDrag : 
  555.                                         IF (MyWindow = tempWindow) AND (tempWindow <> NIL) THEN
  556.                                             BEGIN
  557.                                                 SetRect(tempRect, -32000, -32000, 32000, 32000);
  558.                                                 DragWindow(tempWindow, tempEvent.where, tempRect);
  559.                                             END;
  560.                                     OTHERWISE
  561.                                 END; { of tempCode case }
  562.                             END; { of mouseDown }
  563.                         keydown : 
  564.                             BEGIN
  565.                                 CalcKey(tempEvent.message, tempBus, tempVirtual, tempChar);
  566.                                 IF BitAnd(tempEvent.modifiers, cmdKey) <> 0 THEN
  567.                                     MainMenu(MenuKey(tempChar))
  568.                                 ELSE IF NotSpecKeys(tempVirtual) THEN
  569.                                     FOR tempCode := 1 TO theNum DO
  570.                                         IF (theBus[tempCode] = tempBus) AND (theNum > (tempCode - 1)) THEN
  571.                                             DoKey(tempCode, tempChar)
  572.                             END;
  573.                         updateEvt : 
  574.                             BEGIN
  575.                                 tempWindow := WindowPtr(tempEvent.message);
  576.                                 GetPort(tempPort);
  577.                                 SetPort(tempWindow);
  578.                                 BeginUpdate(tempWindow);
  579.                                 IF (tempWindow = MyWindow) AND (tempWindow <> NIL) THEN
  580.                                     DoUp;
  581.                                 EndUpdate(tempWindow);
  582.                                 SetPort(tempPort);
  583.                             END;
  584.                         OTHERWISE
  585.                     END;
  586.                 END;
  587.         UNTIL Done;
  588.     END;
  589.  
  590. {Game over.}
  591.     PROCEDURE DoQuit;
  592.         VAR
  593.             n : integer;
  594.     BEGIN
  595.         DeleteMenu(AppleMenuID);
  596.         DeleteMenu(FileMenuID);
  597.         DeleteMenu(EditMenuID);
  598.         IF MyWindow <> NIL THEN
  599.             DisposeWindow(MyWindow);
  600.     END;
  601.  
  602. {Main Body}
  603. BEGIN
  604.     DoSetup;
  605.     DoAbout;
  606.     DoConfigure;
  607.     IF NOT done THEN
  608.         BEGIN
  609.             DoStart;
  610.             DoMainLoop;
  611.         END;
  612.     DoQuit;
  613. END.